package com.changgou.oauth.service.impl;

import com.changgou.oauth.service.AuthService;
import com.changgou.oauth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 认证实现类
 * @author 20201115
 */
@Service
public class AuthServiceImpl implements AuthService {


    private RestTemplate restTemplate;
    @Autowired
    public void setRestTemplate(RestTemplate restTemplate){
        this.restTemplate = restTemplate;
    }

    private LoadBalancerClient loadBalancerClient;
    @Autowired
    public AuthServiceImpl(LoadBalancerClient loadBalancerClient){
        this.loadBalancerClient = loadBalancerClient;
    }

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Value("${auth.ttl}")
    private Long ttl;

    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {

        //1、申请令牌
        //1.1 进行 路径的构建
        ServiceInstance serviceInstance = loadBalancerClient.choose("user-auth");
        URI uri = serviceInstance.getUri();
        String url = uri + "/oauth/token";

        //1.2 进行 用户数据的封装
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type","password");
        body.add("username",username);
        body.add("password",password);

        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.add("Authorization",this.getHttpBasic(clientId,clientSecret));
        HttpEntity<MultiValueMap<String,String>> restEntity = new HttpEntity<>(body,headers);

        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, restEntity, Map.class);

        //1.3 当后端 出现了 400 401 ，后端不对这两个异常编码 进行处理，直接返回给前端
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401){
                    super.handleError(response);
                }
            }
        });

        //2、进行令牌信息的封装
        Map map = responseEntity.getBody();
        if (map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null){
            throw new RuntimeException("申请令牌信息失败！！！");
        }
        AuthToken authToken = new AuthToken();
        authToken.setAccessToken((String) map.get("access_token"));
        authToken.setRefreshToken((String) map.get("refresh_token"));
        authToken.setJti((String) map.get("jti"));

        //3、将 令牌 的jti 作为key，access_token作为 value，存入到 redis中
        // boundValueOps(authToken.getJti()) ： key
        // set(authToken.getAccessToken(),ttl, TimeUnit.SECONDS) ：value, 过期时间, 单位
        stringRedisTemplate.boundValueOps(authToken.getJti()).set(authToken.getAccessToken(),ttl, TimeUnit.SECONDS);

        return authToken;
    }

    private String getHttpBasic(String clientId, String clientSecret) {
        String value = clientId + ":" + clientSecret;
        //进行 base64 编码
        byte[] encode = Base64Utils.encode(value.getBytes());
        //转换成 string ，进行 数据返回
        return "Basic " + new String(encode);
    }
}
